package com.why.product.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.why.product.service.CategoryBrandRelationService;
import com.why.product.vo.CateJsonVo;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.why.common.utils.PageUtils;
import com.why.common.utils.Query;

import com.why.product.dao.CategoryDao;
import com.why.product.entity.CategoryEntity;
import com.why.product.service.CategoryService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;


@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {
    @Autowired
    CategoryBrandRelationService categoryBrandRelationService;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    RedissonClient redisson;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 减少和数据库交流，查出所有的分类  // TODO: 2021/4/25    我是感觉应该将数据全部交给服务器好些吧。。。
     *
     * @return
     */
    @Override
    public List<CategoryEntity> listByTree() {
        QueryWrapper<CategoryEntity> wrapper = new QueryWrapper<>();
        wrapper.select("cat_id", "name", "parent_cid", "cat_level", "sort", "icon", "product_unit", " product_count");
        wrapper.eq("show_status", 1);
        List<CategoryEntity> entities = baseMapper.selectList(wrapper);


        //使用流式写法
        //menu类似局部变量 和for循环中的i类似   此时menu就是parent了

        /**
         * 将list集合变为流然后对其进行过滤，获取父id为0的cat，接着peek出来，通过递归设置其孩子
         * 接着按照指定规则sort，最后将其收集起来
         */
        List<CategoryEntity> parents = entities
                .stream()
//                过滤 对于每个父id为0的，都会为其找儿子
                .filter(categoryEntity -> categoryEntity.getParentCid() == 0)
                .peek((menu) -> {
                    menu.setChildren(getChildren(entities, menu));
                    //return menu;
                }).sorted((o1, o2) -> {
                    return (o1.getSort() == null ? 0 : o1.getSort()) - (o2.getSort() == null ? 0 : o2.getSort());
                }).collect(Collectors.toList());


        //老版本写法
//        for (CategoryEntity parent : parents) {
//            parent.setChildren(getChildren(entities, parent));
//        }
        //System.out.println(parents);
        return parents;

    }

    @Override
    public void removeMenuByIds(List<Long> asList) {
        //TODO  1、检查当前删除的菜单，是否被别的地方引用

        //应该使用逻辑删除，而不是物理删除
        baseMapper.deleteBatchIds(asList);
    }

    @Override
    public Long[] findCatelogPath(Long catelogId) {
        List<Long> paths = new ArrayList<>();
        List<Long> parentPath = findParentPath(catelogId, paths);

        Collections.reverse(parentPath);


        return parentPath.toArray(new Long[parentPath.size()]);
    }

    //225,25,2
    private List<Long> findParentPath(Long catelogId, List<Long> paths) {
        //1、收集当前节点id
        paths.add(catelogId);
        CategoryEntity byId = this.getById(catelogId);
        if (byId.getParentCid() != 0) {
            findParentPath(byId.getParentCid(), paths);
        }
        return paths;

    }

    /**
     * @param categoryEntities
     * @param parent
     * @return
     */
    private List<CategoryEntity> getChildren(List<CategoryEntity> categoryEntities, CategoryEntity parent) {


        List<CategoryEntity> children = categoryEntities.stream()
                .filter(categoryEntity -> categoryEntity.getParentCid().equals(parent.getCatId()))
                .peek(categoryEntity -> {
                    //递归调用为当前孩子设置孩子
                    categoryEntity.setChildren(getChildren(categoryEntities, categoryEntity));
                })
                .sorted((o1, o2) -> {
                    return (o1.getSort() == null ? 0 : o1.getSort()) - (o2.getSort() == null ? 0 : o2.getSort());
                })
                .collect(Collectors.toList());
        return children;

//        List<CategoryEntity> result = new ArrayList<>();
//
//        for (CategoryEntity entity : categoryEntities) {
//
//            if (parent.getCatId().equals(entity.getParentCid())) {
//                result.add(entity);
//                entity.setChildren(getChildren(categoryEntities, entity));
//            }
//
//        }


    }


    /**
     * 级联更新所有关联的数据，也就是当数据更新的时候 删除getLeveloneCategories方法所对应的缓存
     * 我们当更新分类的时候要同时删除 updateCascade和getLeveloneCategories方法的缓存
     * 因此我们要同时清空2个缓存
     *
     * @param category
     */
//    @CacheEvict(value = "category",key = "'getLeveloneCategories'") 这个只能删除一个
//    value相当于分区，而key是分区中的标志位
//
//    @Caching(
//            evict = {
//                    @CacheEvict(value = "category",key = "'getLeveloneCategories'"),
//                    @CacheEvict(value = "category",key = "'getCatalogJson'"),
//            }
//
//    )           //多操作

    @CacheEvict(value = "category",allEntries = true)  //会删除分区category中的所有数据
    @Transactional
    @Override
    public void updateCascade(CategoryEntity category) {
        this.updateById(category);
        categoryBrandRelationService.updateCategory(category.getCatId(), category.getName());
    }

    /**
     * key = "'level1'" 里面的 level1要加上单引号'',不然就会将其当作表达式进行动态取值
     *
     * @return
     */
    @Cacheable(value = {"category"}, key = "#root.method.name")
    @Override
    public List<CategoryEntity> getLeveloneCategories() {
        QueryWrapper<CategoryEntity> result = new QueryWrapper<CategoryEntity>().eq("parent_cid", 0);
        return baseMapper.selectList(result);
    }

    /**
     * 使用缓存注解,value相当于分区
     *
     * @return
     */
    @Cacheable(value = {"category"}, key = "#root.method.name")
    @Override
    public Map<String, List<CateJsonVo>> getCatalogJson() {
        //        查到所有的分类
        List<CategoryEntity> categoryEntities = baseMapper.selectList(null);
//        获取一级的id
        List<CategoryEntity> categoryLevel1 = getBypCid(0L, categoryEntities);
//        k是一级分类的id，value我们自己定义,将其封装成一个CateJsonVo
        Map<String, List<CateJsonVo>> collect = categoryLevel1.stream().collect(Collectors.toMap(k -> {
            return k.getCatId().toString();
        }, v -> {
            // 二级放一级，根据一级分类的id封装二级分离
            List<CategoryEntity> categoryLevel2 = getBypCid(v.getCatId(), categoryEntities);
//
            List<CateJsonVo> cateJsonVoList = categoryLevel2.stream().map(item -> {
                CateJsonVo cateJsonVo = new CateJsonVo();
                cateJsonVo.setCatalog1Id(item.getParentCid().toString());
                cateJsonVo.setId(item.getCatId().toString());
                cateJsonVo.setName(item.getName());
                // 三级放二级
                List<CategoryEntity> categoryLevel3 = getBypCid(item.getCatId(), categoryEntities);
                List<CateJsonVo.Category3Vo> catelog3Lists = categoryLevel3.stream().map(categoryEntity -> {
                    CateJsonVo.Category3Vo catelog3List = new CateJsonVo.Category3Vo();
                    catelog3List.setCatalog2Id(categoryEntity.getParentCid().toString());
                    catelog3List.setId(categoryEntity.getCatId().toString());
                    catelog3List.setName(categoryEntity.getName());
                    return catelog3List;
                }).collect(Collectors.toList());
                cateJsonVo.setCatalog3List(catelog3Lists);
                return cateJsonVo;
            }).collect(Collectors.toList());
            return cateJsonVoList;
        }));
        return collect;
    }

    /**
     * todo 堆外内存溢出
     *
     * @return
     */


    public Map<String, List<CateJsonVo>> getCatalogJson2() {
        RLock lock = redisson.getLock("catJsonLock");
        try {

            //        加锁
            lock.lock();
            String catJson = redisTemplate.opsForValue().get("catJson");
            if (StringUtils.isEmpty(catJson)) {
                Map<String, List<CateJsonVo>> catalog = getCatalogFromDb();
                redisTemplate.opsForValue().set("catJson", JSON.toJSONString(catalog));
                return catalog;
            }
            /**
             * new TypeReference<Map<String, List<CateJsonVo>>>() {}  构造器受保护 要创建一个匿名内部类or实现类来使用
             */
            Map<String, List<CateJsonVo>> map = JSON.parseObject(catJson,
                    new TypeReference<Map<String, List<CateJsonVo>>>() {
                    });

            return map;
        } finally {
            // 解锁
            lock.unlock();
        }

    }


    public Map<String, List<CateJsonVo>> getCatalogFromDb() {


        //        查到所有的分类
        List<CategoryEntity> categoryEntities = baseMapper.selectList(null);
//        获取一级的id
        List<CategoryEntity> categoryLevel1 = getBypCid(0L, categoryEntities);
//        k是一级分类的id，value我们自己定义,将其封装成一个CateJsonVo
        Map<String, List<CateJsonVo>> collect = categoryLevel1.stream().collect(Collectors.toMap(k -> {
            return k.getCatId().toString();
        }, v -> {
            // 二级放一级，根据一级分类的id封装二级分离
            List<CategoryEntity> categoryLevel2 = getBypCid(v.getCatId(), categoryEntities);
//
            List<CateJsonVo> cateJsonVoList = categoryLevel2.stream().map(item -> {
                CateJsonVo cateJsonVo = new CateJsonVo();
                cateJsonVo.setCatalog1Id(item.getParentCid().toString());
                cateJsonVo.setId(item.getCatId().toString());
                cateJsonVo.setName(item.getName());
                // 三级放二级
                List<CategoryEntity> categoryLevel3 = getBypCid(item.getCatId(), categoryEntities);
                List<CateJsonVo.Category3Vo> catelog3Lists = categoryLevel3.stream().map(categoryEntity -> {
                    CateJsonVo.Category3Vo catelog3List = new CateJsonVo.Category3Vo();
                    catelog3List.setCatalog2Id(categoryEntity.getParentCid().toString());
                    catelog3List.setId(categoryEntity.getCatId().toString());
                    catelog3List.setName(categoryEntity.getName());
                    return catelog3List;
                }).collect(Collectors.toList());
                cateJsonVo.setCatalog3List(catelog3Lists);
                return cateJsonVo;
            }).collect(Collectors.toList());
            return cateJsonVoList;
        }));
        return collect;


    }

    private List<CategoryEntity> getBypCid(Long pCid, List<CategoryEntity> categoryEntities) {
        List<CategoryEntity> collect = categoryEntities.stream().filter(item -> {
            return item.getParentCid().equals(pCid);
        }).collect(Collectors.toList());
        return collect;
    }


}